
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:suburban_inside_flutter/viewmodel/history_sort_state.dart';
import 'dart:typed_data';

import '../model/save_info_model.dart';

enum RespondStatus {
  ok,
  frontendErr,
  backendOff,
  backendErr,
}

class GeneralViewModel {

  GeneralViewModel._() {
    frontendCommands = {
      "cls": cmd_cls,
    };
  }
  static final _instance = GeneralViewModel._();
  factory GeneralViewModel() => _instance;

  // double get currentTotalReward => 0.68;
  // double get currentTotalCost => 0.89;

  final respondScrollViewmodel = RespondsScrollViewmodel();
  final selectActionState = SelectActionState();
  final obsImageViewmodel = ObsImageViewmodel();
  final hotMemoryState = HotMemoryState();
  final historyViewmodel = HistoryPageViewmodel();

  final List<String> commandNeedRefreshObs = ['step', 'undo', 'redo', 'reset', 'restore', 'random', 'predict'];
  late final Map<String, void Function(List<String>)> frontendCommands;

  Future<void> pushRequest(String commandString) async {
    // todo: do real request
    var now = DateTime.now();

    List<String> slices = commandString.split(" ");

    if (slices.length==1 && slices[0] == "") {
      respondScrollViewmodel.add(
          ResponseModel(commandString, "No command detected.", RespondStatus.frontendErr, now)
      );
      return;
    }

    print("push_request received: $slices");
    String command = slices.removeAt(0);

    if (frontendCommands.containsKey(command)) {
      frontendCommands[command]!.call(slices);
      return;
    }


    try {

      String url;
      if (slices.isEmpty) {
        url = 'http://127.0.0.1:5000/cmd/$command';
      }
      else {
        url = 'http://127.0.0.1:5000/cmd/$command/${slices.join("\$")}';
      }

      // 发送 GET 请求
      await http.get(Uri.parse(url)).then((response) {
        // 检查响应状态码
        if (response.statusCode == 200) {
          // 解析 JSON 数据
          // final data = json.decode(response.body);
          print("received text: ${response.body}");
          respondScrollViewmodel.add(
              ResponseModel(commandString, response.body, RespondStatus.ok)
          );
          if (commandNeedRefreshObs.contains(command)) {
            obsImageViewmodel.notify();
          }
          print("undo finished hit");
          return;

        } else {
          respondScrollViewmodel.add(
            ResponseModel(commandString, "code ${response.statusCode}", RespondStatus.backendOff)
          );
          return;
        }
      });


    } catch (e) {
      respondScrollViewmodel.add(
          ResponseModel(commandString, "frontend err: $e", RespondStatus.frontendErr)
      );
      return;
    }
  }

  void cmd_cls(List<String> args) {
    respondScrollViewmodel.cls();
  }

  Future<List<(double, double, double)>?> getAllActions() async {
    try {
      // 发送 GET 请求获取图片数据
      final response = await http.get(Uri.parse('http://127.0.0.1:5000/all_actions'));

      // 检查响应状态码
      if (response.statusCode == 200) {
        final List<dynamic> jsonData = json.decode(response.body);
        print("all_actions received: $jsonData");
        return jsonData.map((item) {
          final List<dynamic> values = item;
          return (values[0] as double, values[1] as double, values[2] as double);
        }).toList();
      } else {
        GeneralViewModel().respondScrollViewmodel.add(
            ResponseModel("", "all_actions fetch network err - code ${response.statusCode}", RespondStatus.backendOff)
        );
      }
    } catch (e) {
      GeneralViewModel().respondScrollViewmodel.add(
          ResponseModel("", "all_actions fetch frontend err - $e", RespondStatus.frontendErr)
      );
    }
    return null;
  }

}



class ObsImageViewmodel extends ChangeNotifier {

  Uint8List? oldImage;
  List<(double, double, double)> _actions = [];
  List<(double, double, double)> get actions => _actions;

  Future<Uint8List?> updateImage() async {
    try {
      // 发送 GET 请求获取图片数据
      final response = await http.get(Uri.parse('http://127.0.0.1:5000/current_obs_plus/256'));

      // 检查响应状态码
      if (response.statusCode == 200) {
        oldImage = response.bodyBytes;

        print("WARNING - Updating Actions in ObsImageViewModel.updateImage()");
        GeneralViewModel().getAllActions().then((data) {
          if (data!=null) {
            _actions = data;
          }
        });

        return oldImage;
      } else {
        GeneralViewModel().respondScrollViewmodel.add(
            ResponseModel("", "obs fetch network err - code ${response.statusCode}", RespondStatus.backendOff)
        );
        throw "bad network";
      }
    } catch (e) {
      GeneralViewModel().respondScrollViewmodel.add(
          ResponseModel("", "obs fetch frontend err - $e", RespondStatus.frontendErr)
      );
      rethrow;
    }
  }

  Future<(String, String, String)> updateTotalState() async {
    try {
      // 发送 GET 请求获取图片数据
      final response = await http.get(Uri.parse('http://127.0.0.1:5000/total_state'));

      // 检查响应状态码
      if (response.statusCode == 200) {
        var totalState = response.body;
        List<String> slices = totalState.split("_");
        assert(slices.length==3);
        return (slices[0], slices[1], slices[2]);
      } else {
        GeneralViewModel().respondScrollViewmodel.add(
            ResponseModel("", "total_state fetch network err - code ${response.statusCode}", RespondStatus.backendOff)
        );
        throw "bad network";
      }
    } catch (e) {
      GeneralViewModel().respondScrollViewmodel.add(
          ResponseModel("", "total_state fetch frontend err - $e", RespondStatus.frontendErr)
      );
      rethrow;
    }
  }

  void notify() {
    notifyListeners();
  }
}



class HotMemoryState extends ChangeNotifier {

  bool _enable = false;
  List<(double, double, double)> _actions = [];
  bool get enable => _enable;

  void tap() async {
    if (_enable) {
      _enable = false;
      notifyListeners();

      await GeneralViewModel().pushRequest("reset");
      for (var action in _actions) {
        await GeneralViewModel().pushRequest("step ${action.$1} ${action.$2} ${action.$3}");
      }

      _actions = [];
    }
    else {
      // GeneralViewModel().getAllActions().then((data) {
      //   if (data!=null) {
      //     if (data.isNotEmpty) {
      //       _actions = data;
      //       _enable = true;
      //       notifyListeners();
      //     }
      //     else {
      //       GeneralViewModel().respondScrollViewmodel.add(
      //         ResponseModel("", "No need to hot-save.", RespondStatus.frontendErr)
      //       );
      //     }
      //   }
      // });
      var temp = GeneralViewModel().obsImageViewmodel.actions;
      if (temp.isNotEmpty) {
        _actions = temp;
        _enable = true;
        notifyListeners();
      }
      else {
        GeneralViewModel().respondScrollViewmodel.add(
            ResponseModel("", "No need to hot-save.", RespondStatus.frontendErr)
        );
      }
    }
  }

}


class SelectActionState extends ChangeNotifier {

  Offset? _selectXY;
  double? _radius;
  final double initRadius = 0.5;
  bool get selecting => _selectXY!=null;
  Offset get offset => _selectXY ?? const Offset(0, 0);
  String get x => "${_selectXY?.dx.toStringAsFixed(3)}";
  String get y => "${_selectXY?.dy.toStringAsFixed(3)}";
  double get radius => linear(_radius ?? initRadius, _currentMin, _currentMax);
  double get radius_raw => _radius ?? initRadius;
  String get currentTypeName => _currentTypeName;

  String _currentTypeName = "";
  double _currentMin = 0;
  double _currentMax = 0;
  double linear(double radius, double min, double max) {
    return min + radius * (max - min);
  }

  Future<(double, double)?> fetchCurrentRadiusLinear() async {
    try {
      // 发送 GET 请求获取图片数据
      final response = await http.get(Uri.parse('http://127.0.0.1:5000/current_radius_linear'));

      // 检查响应状态码
      if (response.statusCode == 200) {
        String data = response.body;
        List<String> slices = data.split("_");
        assert(slices.length == 2);
        return (double.parse(slices[0]), double.parse(slices[1]));
      } else {
        GeneralViewModel().respondScrollViewmodel.add(
            ResponseModel("", "radius linear network err - code ${response.statusCode}", RespondStatus.backendOff)
        );
      }
    } catch (e) {
      GeneralViewModel().respondScrollViewmodel.add(
          ResponseModel("", "radius linear frontend err - $e", RespondStatus.frontendErr)
      );
    }
    return null;
  }


  Future<String?> fetchCurrentSpaceType() async {
    try {
      // 发送 GET 请求获取图片数据
      final response = await http.get(Uri.parse('http://127.0.0.1:5000/current_space_type'));

      // 检查响应状态码
      if (response.statusCode == 200) {
        return response.body;
      } else {
        GeneralViewModel().respondScrollViewmodel.add(
            ResponseModel("", "space name network err - code ${response.statusCode}", RespondStatus.backendOff)
        );
      }
    } catch (e) {
      GeneralViewModel().respondScrollViewmodel.add(
          ResponseModel("", "space name frontend err - $e", RespondStatus.frontendErr)
      );
    }
    return null;
  }

  Future<void> restoreLastAction({required Future<void> Function() undoImplementation}) async {
    return await GeneralViewModel().getAllActions().then((data) {
      if (data != null) {
        if (data.isNotEmpty) {
          var lastAction = data[data.length-1];
          print("restore_last_action: $lastAction");
          undoImplementation.call().then((_) {
            print("start restore selecting hit");
            select(Offset(lastAction.$1, lastAction.$2), lastAction.$3);
          });
        }
        else {
          print("restore data is empty");
        }
      }
    });
  }

  void select(Offset xy, [double? radius]) {
    _selectXY = xy;
    _radius = radius;
    notifyListeners();

    fetchCurrentRadiusLinear().then((record) {
      if (record != null) {
        var (min, max) = record;
        _currentMin = min;
        _currentMax = max;
        notifyListeners();
      }
    });

    fetchCurrentSpaceType().then((name) {
      if (name != null) {
        _currentTypeName = name;
        notifyListeners();
      }
    });

  }

  void refreshRadius(double radius) {
    _radius = radius;
    print("value changed to $radius");
    notifyListeners();
  }

  void cancel() {
    _selectXY = null;
    _radius = null;
    notifyListeners();
    _currentMin = 0;
    _currentMax = 0;
    _currentTypeName = "";
  }

  void step() {
    print("selectActionState: step called");
    GeneralViewModel().pushRequest(
      "step ${_selectXY!.dx.toStringAsFixed(3)} ${_selectXY!.dy.toStringAsFixed(3)} $radius_raw"
    );
    cancel();
  }

}



class RespondsScrollViewmodel extends ChangeNotifier{
  final List<ResponseModel> _responds = [
    // ResponseModel("user cmd request", "ok.", RespondStatus.ok),
    // ResponseModel("user cmd request", "backend off!", RespondStatus.backendOff),
    // ResponseModel("user cmd request", "backend err!", RespondStatus.backendErr),
    // ResponseModel("user cmd request", "ok.", RespondStatus.ok),
    // ResponseModel("user cmd request", "backend off!", RespondStatus.backendOff),
    // ResponseModel("user cmd request", "backend err!", RespondStatus.backendErr),
  ];

  List<ResponseModel> get responds => _responds.reversed.toList();

  void notify() {
    notifyListeners();
  }

  void add(ResponseModel model) {
    _responds.add(model);
    notifyListeners();
  }

  void cls() {
    _responds.clear();
    notifyListeners();
  }
}


class HistoryPageViewmodel extends ChangeNotifier {

  List<SaveInfo> _infos = [];
  final Map<String, Uint8List?> _covers = {};
  List<SaveInfo> get infos => historySortState.sort(_infos);
  Map<String, Uint8List?> get covers => _covers;

  final historySortState = HistorySortState();
  void updateSortType(SortType sortType) {
    historySortState.switchType(sortType);
    notifyListeners();
  }


  Future<void> fetch() async {
    try {
      // 发送 GET 请求获取图片数据
      final response = await http.get(Uri.parse('http://127.0.0.1:5000/all_info'));

      // 检查响应状态码
      if (response.statusCode == 200) {
        // final List<dynamic> jsonData = json.decode(response.body);
        final jsonList = response.body;
        _infos = SaveInfo.fromJsonList(jsonList).reversed.toList();
        _covers.clear();
        for (var info in _infos) {
          _covers[info.name] = await getCover(info.name);
        }
        return;
      } else {
        GeneralViewModel().respondScrollViewmodel.add(
            ResponseModel("", "history fetch network err - code ${response.statusCode}", RespondStatus.backendOff)
        );
      }
    } catch (e) {
      GeneralViewModel().respondScrollViewmodel.add(
          ResponseModel("", "history fetch frontend err - $e", RespondStatus.frontendErr)
      );
    }
    return;
  }

  Future<Uint8List?> getCover(String name) async {
    try {
      // 发送 GET 请求获取图片数据
      final response = await http.get(Uri.parse('http://127.0.0.1:5000/cover/$name'));

      // 检查响应状态码
      if (response.statusCode == 200) {
        var image = response.bodyBytes;
        return image;
      } else {
        throw "bad network";
      }
    } catch (e) {
      rethrow;
    }
  }

  void remove(String name) {
    GeneralViewModel().pushRequest("remove $name").then((_) {
      notifyListeners();
    });
  }

  void notify() {
    notifyListeners();
  }

}


class ResponseModel {

  final String request;
  final String response;
  final RespondStatus status;
  late final DateTime requestTime;

  ResponseModel(this.request, this.response, this.status, [DateTime? requestTime]) {
    this.requestTime = requestTime ?? DateTime.now();
  }

  @override
  String toString() {
    return "[$request, $response}]";
  }
}

